package org.xyz.service;

import cn.hutool.core.lang.UUID;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.app.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.xyz.entity.WXPayOrderReqVO;
import org.xyz.entity.WxPayRespVO;
import org.xyz.entity.WxPayV3Bean;
import org.xyz.util.WXPayUtil;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.wechat.pay.java.core.http.Constant.*;

/**
 * @description: 微信支付
 * @link: https://blog.csdn.net/m0_61470934/article/details/132832522
 * @create: 2024/1/20 14:28
 **/
@Component
@Slf4j
public class WxPayService_ {

    private RSAAutoCertificateConfig notificationConfig;

    @Autowired
    private WxPayV3Bean wxPayV3Bean;

    @Value("${wx.session_key_url}")
    private String sessionKeyUrl;

    /**
     * 商户号
     */
    @Value("${wx.mch_id}")
    public String merchantId;

    /**
     * 商户API私钥路径
     */
    @Value("${wx.private_key_path}")
    public String privateKeyPath;

    /**
     * 商户证书序列号
     */
    @Value("${wx.serial_number}")
    public String merchantSerialNumber;

    /**
     * 商户APIV3密钥
     */
    @Value("${wx.api_V3_key}")
    public String apiV3Key;

    /**
     * 通知地址
     */
    @Value("${wx.notify_url}")
    public String notifyUrl;

    /**
     * 小程序ID
     */
    @Value("${wx.app_id}")
    public String wxAppid;


    /**
     * 拉起微信小程序预付款信息
     *
     * @param orderNo    订单编号
     * @param totalPrice 支付的价格（字符串例如：25.66）
     * @param openId     微信openId
     * @return 预支付ID和openId的map
     */
    public Map<String, Object> wxSmartPay(String orderNo, String totalPrice, String openId) {
        Map<String, Object> map = new HashMap<>();
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(notificationConfig).build();
        PrepayRequest request = new PrepayRequest();
        request.setAppid(wxAppid);
        request.setMchid(merchantId);
        request.setDescription("商品描述");
        request.setOutTradeNo(orderNo);
        request.setNotifyUrl(notifyUrl);
        Amount amount = new Amount();
        amount.setTotal(convertToCents(totalPrice));
        amount.setCurrency("CNY");
        request.setAmount(amount);
        Payer payer = new Payer();
        payer.setOpenid(openId);
        request.setPayer(payer);
        log.info("小程序调用微信支付请求参数：{}", JSON.toJSONString(request));
        PrepayWithRequestPaymentResponse requestPaymentResponse = service.prepayWithRequestPayment(request);
        log.info("小程序调用微信支付响应结果：{}", JSON.toJSONString(requestPaymentResponse));
        // requestPaymentResponse对象中包含小程序中拉起微信支付的参数
        map.put("data", requestPaymentResponse);
        map.put("openId", openId);
        return map;
    }

    public WxPayRespVO createOrder(WXPayOrderReqVO req) throws Exception {
        try {
            // 使用自动更新平台证书的RSA配置
            // 一个商户号只能初始化一个配置，否则会因为重复的下载任务报错
            Config config =
                    new RSAAutoCertificateConfig.Builder()
                            .merchantId(merchantId)
                            .privateKeyFromPath(privateKeyPath)
                            .merchantSerialNumber(merchantSerialNumber)
                            .apiV3Key(apiV3Key)
                            .build();
            // 构建service
            JsapiService service = new JsapiService.Builder().config(config).build();
            // request.setXxx(val)设置所需参数，具体参数可见Request定义
            PrepayRequest request = new PrepayRequest();
            Amount amount = new Amount();
            amount.setTotal(1); //req.getTotalPrice()
            request.setAmount(amount);
            request.setAppid(wxAppid);
            request.setMchid(merchantId);
            request.setDescription(req.getGoodsName());
            request.setNotifyUrl(notifyUrl);
            request.setOutTradeNo(req.getOrderSn());
            request.setAttach(String.valueOf(req.getOrderType()));
            Payer payer = new Payer();
            payer.setOpenid(WXPayUtil.getOpenId(req.getWxCode()));
            request.setPayer(payer);
            // 调用下单方法，得到应答
            PrepayResponse response = service.prepay(request);
            WxPayRespVO vo = new WxPayRespVO();
            String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
            vo.setTimeStamp(timeStamp);
            String substring = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
            vo.setNonceStr(substring);
            String signatureStr = Stream.of(wxPayV3Bean.getAppId(), String.valueOf(timeStamp), substring, "prepay_id=" + response.getPrepayId())
                    .collect(Collectors.joining("\n", "", "\n"));
            String sign = WXPayUtil.getSign(signatureStr, privateKeyPath);
            vo.setPaySign(sign);
            vo.setPrepayId("prepay_id=" + response.getPrepayId());
            return vo;
        }catch (ServiceException e){
            JSONObject parse = JSONObject.parseObject(e.getResponseBody());
            throw new Exception(parse.getString("message"));
//            throw new ResultException(ResultEnum.ERROR,parse.getString("message"));
        }catch (Exception e){
            throw new Exception(e.getMessage());
//            throw new ResultException(ResultEnum.ERROR,e.toString());
        }
    }

    public void payNotify(HttpServletRequest request) {
        try {
            //读取请求体的信息
            ServletInputStream inputStream = request.getInputStream();
            StringBuffer stringBuffer = new StringBuffer();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String s;
            //读取回调请求体
            while ((s = bufferedReader.readLine()) != null) {
                stringBuffer.append(s);
            }
            String s1 = stringBuffer.toString();
            String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
            String nonce = request.getHeader(WECHAT_PAY_NONCE);
            String signType = "RSA";request.getHeader("Wechatpay-Signature-Type");
            String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
            String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
            // 如果已经初始化了 RSAAutoCertificateConfig，可直接使用
            // 没有的话，则构造一个
            log.error(JSON.toJSONString(wxPayV3Bean));
            NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                    .merchantId(wxPayV3Bean.getMchId())
                    .privateKeyFromPath(wxPayV3Bean.getKeyPath())
                    .merchantSerialNumber(wxPayV3Bean.getMchSerialNumber())
                    .apiV3Key(wxPayV3Bean.getApiKey())
                    .build();
            // 初始化 NotificationParser
            NotificationParser parser = new NotificationParser(config);
            RequestParam requestParam=new RequestParam.Builder()
                    .serialNumber(serialNo)
                    .nonce(nonce)
                    .signature(signature)
                    .timestamp(timestamp)
                    // 若未设置signType，默认值为 WECHATPAY2-SHA256-RSA2048
                    .signType(signType)
                    .body(s1)
                    .build();
            Transaction parse = parser.parse(requestParam, Transaction.class);
            System.out.println("parse = " + parse);
        } catch (Exception e) {
//            throw new ResultException(ResultEnum.ERROR,e.toString());
//            throw new Exception(e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 接受微信通知
     *
     * @param requestParam 微信服务器请求过来的参数
     * @return 解密之后的参数对象
     */
    public Transaction wxNotify(RequestParam requestParam) {
        NotificationParser parser = new NotificationParser(notificationConfig);
        Transaction transaction;
        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            transaction = parser.parse(requestParam, Transaction.class);
        } catch (Exception e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            log.info("微信通知，签名校验失败", e);
            return null;
        }
        log.info("微信通知，解密结果：{}", JSON.toJSONString(transaction));
        return transaction;
    }


    /**
     * 转换价格
     *
     * @param totalPrice
     * @return
     */
    public static Integer convertToCents(String totalPrice) {
        BigDecimal bigDecimal = new BigDecimal(totalPrice);
        BigDecimal multipliedBy100 = bigDecimal.multiply(new BigDecimal(100));
        return multipliedBy100.setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
    }

    /**
     * 获取微信openId
     *
     * @param code
     * @return
     */
    public String getOpenId(String code) {
        try {
            String res = HttpUtil.get(String.format(sessionKeyUrl, code));
            log.debug("获取WX_Session_Key结束，结果：{}", res);
            JSONObject jsonObject = JSON.parseObject(res);
            if (jsonObject.containsKey("openid")) return jsonObject.get("openid").toString();
            else return "";
        } catch (Exception e) {
            log.error("wxCode解密失败：code->{}", code);
            return "";
        }
    }


    /**
     * 初始化微信支付配置
     */
    /*@PostConstruct
    public void init() {
        // RSAAutoCertificateConfig对象为自动更新微信平台证书的对象，可发起微信支付调用，也可以接受通知进行解析
        notificationConfig = new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKeyFromPath(privateKeyPath)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .build();
    }*/

}

